--[[-------------------------------------------------------------------------
  Copyright (c) 2007, Matthew Carras (matthew.carras AT gmail.com)
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above
        copyright notice, this list of conditions and the following
        disclaimer in the documentation and/or other materials provided
        with the distribution.
      * Neither the name or handles of the author nor the names or handles of
        its contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------]]

local bot = "Luaircbot"
local rev = "1"

--[[-------------------------------------------------------------------
-- Pirates go ARGGGH for ARGGGuments
--]]

if not arg[1] then
    print()
    print(bot .. " r" .. rev)
    print("http://code.google.com/p/luaircbot/")
    print("Usage: lua " .. arg[0] .. " <lua configuration file>")
    print()
    return
end

--[[-------------------------------------------------------------------
-- I need your identification, ma'am
--]]

print("--------------------------------------------------------")
print("-- " .. bot .. " r" .. rev)
print("--")
print("-- http://luaircbot.codegoogle.com/svn/trunk/")
print("--------------------------------------------------------")
print()

--[[-------------------------------------------------------------------
-- [Module Framework] Invigorates Hair And Scalp
--]]

local lastPrintTime = 0
local verbosityLevels = { ["FATAL"]=0, ["ERROR"]=1, ["STATUS"]=2, ["NOTIFY"]=3, ["DEBUG"]=4, ["TRACE"]=5, ["ALL"]=6 }
local maxVerbosity = "DEBUG" -- just a default, this can be changed
local function Print(self,text,verbosity)
    if not text then return end
    if not verbosity or verbosityLevels[verbosity] <= verbosityLevels[maxVerbosity] then
        print("[" .. self.name .. "\t: " .. (os.time() - lastPrintTime) .. "] " .. text)
    end
end

-- Everything gets Print.
local mixins = {
    ["Print"] = Print
}

-- generic registration function
local function Register(self, reg, name)
    name = name:lower()
    reg[name] = {}
    reg[name].name = name:upper()
    for n,f in pairs(mixins) do
        reg[name][n] = f
    end
    return reg[name]
end

-- Most modules are frameworks, so they get access to Register.
local modmixins = {
    ["Register"] = Register
}

local modreg = {}
local function RegisterModule(self, name)
    local mod = Register(self, modreg, name)
    for n,f in pairs(modmixins) do
        mod[n] = f
    end
    return mod
end
local function GetModule(self, name)
    return modreg[name]
end

--[[-------------------------------------------------------------------
-- [Private] Perspective
--]]

local mainQueue, timers = {}, {}

-- This queue will process every second.
local function ProcessQueue(self)
    for _,f in ipairs(mainQueue) do
        local ok, result = pcall( f )
        if not ok and result then
            self:Print("(in ProcessQueue) " .. result, "ERROR")
        end
    end
    return true
end

-- These are timed function calls.
-- Note that delay can be a function as well as a value (in seconds)
-- timers[name] = { function to call, delay, lastcall }
local function ProcessTimers(self)
    for n,t in pairs(timers) do
        local delay = t[2]
        if type(delay) == "function" then delay = delay() end
        if os.time() > t[3] + delay then
            local ok, result = pcall( t[1] )
            if not ok and result then
                self:Print( "(in ProcessTimers) " .. result, "ERROR")
            end
        end
    end
    return true
end

--[[-------------------------------------------------------------------
-- Name that [Namespace]
--]]

IrcBot = RegisterModule(nil, "core")
local core = IrcBot
core.RegisterModule = RegisterModule
core.GetModule = GetModule
core.rev = rev
core.core = bot

--[[-------------------------------------------------------------------
-- Core [Method]ology
--]]

local abortmsg = "Aborting due to previous errors"

-- Using loadfile gives a little protection in case of syntax error.
-- Loaded file will have its own filename sent to it.
function core:Load(luafile)
    local dochunk, err = loadfile(luafile)
    if not dochunk then
        self:Print("Could not load " .. luafile, "ERROR")
        if err then error(err) end
        return false
    end

    return dochunk(luafile) or true
end

local halt -- stop the main loop
function core:Halt()
    self:Print("Halt issued.", "FATAL")
    halt = true
    -- do magic here if needed (like save settings, etc.)
end

-- Global message verbosity
-- See Print under Mixins.
function core:SetPrintVerbosity(verbosity)
    if verbosityLevels[verbosity] then
        maxVerbosity = verbosity
    end
end

-- see locals for info on these functions.
function core:Queue(func)
    tinsert(mainQueue, func)
end
function core:RegisterTimer(name, func, delay)
    if not timer[name] then timer[name] = {} end
    timer[name] = { func, delay, 0 }
end
function core:AdjustTimer(name, delay)
    timer[name][2] = delay
end
function core:RemoveTimer(name)
    timer[name] = nil
end
--[[------------------------------------------------------------------
-- [Loading Framework] Is A Go!
--]]

-- Configuration framework and default values.
if not core:Load("config.lua") then
    core:Print(abortmsg, "FATAL")
    return
end
local config = core:GetModule("config")
if not config:Load(arg[1]) then     -- configuration for this instance passed
    core:Print(abortmsg, "FATAL")   -- as 1st argument
    return
end

-- Main IRC framework.
if not core:Load("irc.lua") then
    core:Print(abortmsg, "FATAL")
    return
end
local irc = core:GetModule("irc")
if not irc:Connect() then
    core:Print(abortmsg, "FATAL")
    return
end

-- Addon framework.
-- Loads all addons listed in configuration, and their depencencies.
if not core:Load("addons.lua") then
    core:Print(abortmsg, "FATAL")
    return
end

--[[----------------------------------------------------------------
-- Implementing the [main loop] since 1923 for your protection.
--]]

while not halt do
    if not irc:Receive() then
        core:Print(abortmsg, "FATAL")
        core:Halt()
    end
    if not ProcessQueue(core) then -- non-irc interaction
        core:Print(abortmsg, "FATAL")
        core:Halt()
    end
    if not ProcessTimers(core) then -- timed non-irc interaction
        core:Print(abortmsg, "FATAL")
        core:Halt()
    end
    if not irc:ProcessQueue() then -- irc interaction (self flood prevention)
        core:Print(abortmsg, "FATAL")
        core:Halt()
    end
end

-- Babylon 5 was a kick-ass show.
